﻿using System;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.PlayerLoop;
using XFABManager;

namespace XFGameFramework
{

    public enum AudioAssetType {
        AssetBundle,
        AudioClip
    }

    public enum AudioPlayType { 
        Play,
        PlayOneShot
    }

    public enum AudioPlayPosition {
        IgnorePosition,
        CurrentTransfomrPosition,
        CustomPosition
    }

    public class AudioPlayer : MonoBehaviour
    {

        #region 静态字段

        /// <summary>
        /// 音频文件的引用计数
        /// </summary>
        private static Dictionary<AudioClip, int> AuidoClipRefCount = new Dictionary<AudioClip, int>();

        /// <summary>
        /// 引用计数为0 准备卸载的音频
        /// </summary>
        private static Dictionary<AudioClip, float> ReadyUnloadClip = new Dictionary<AudioClip, float>();

        private static List<AudioClip> TempAudioClipList = new List<AudioClip>();

        private const int UNLOAD_TIME_OUT = 600; // 默认超过十分钟卸载 
        //private const int UNLOAD_TIME_OUT = 10; // 默认超过十分钟卸载 

        private static Coroutine coroutine_check_unload = null;

        #endregion

        #region 静态属性

        private static Dictionary<string, AudioController> AudioControllers = new Dictionary<string, AudioController>();

        #endregion

        #region 静态方法

        /// <summary>
        /// 注册AudioController
        /// </summary>
        /// <param name="name">名称,勿重复!</param>
        /// <param name="loop">是否循环播放</param>
        /// <param name="SpatialBlend">0:2D音效，1:3D音效!</param>
        public static void RegisterAudioController(string name, bool loop, float SpatialBlend = 0)
        {
            if (AudioControllers.ContainsKey(name)) return;
            AudioController config = new AudioController(name, loop, SpatialBlend);
            AudioControllers.Add(config.Name, config);
        }


        /// <summary>
        /// 注册AudioController(使用指定的AudioSource)
        /// </summary>
        /// <param name="name">名称,勿重复!</param>
        /// <param name="audioSource">AudioSource对象，该AudioController会使用这个AudioSourece来播放音频</param>
        /// <param name="worldPositionStays">AudioSource所在的游戏物体的位置是否保持不变,true:不变 false:会放到一个统一的节点下面</param>
        public static void RegisterAudioController(string name, AudioSource audioSource, bool worldPositionStays = true) {
            if (audioSource == null) return;
            if (AudioControllers.ContainsKey(name)) return;
            AudioController config = new AudioController(name, audioSource, worldPositionStays);
            AudioControllers.Add(config.Name, config);
        }

        /// <summary>
        /// 查询AudioController
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static AudioController GetAuidoController(string name)
        {
            if (AudioControllers.ContainsKey(name))
                return AudioControllers[name];
            return null;
        }

        /// <summary>
        /// 移除AudioController
        /// </summary>
        /// <param name="name"></param>
        public static void RemoveAudioController(string name) {
            if (!AudioControllers.ContainsKey(name)) return;
            AudioControllers[name].Remove();
            AudioControllers.Remove(name);
        }

        /// <summary>
        /// 播放音频
        /// </summary>
        /// <param name="controllerName">AudioController名称</param>
        /// <param name="clip">音频</param>
        /// <param name="playOneShot">是否为PlayOneShot</param>
        /// <exception cref="Exception"></exception>
        public static void Play(string controllerName,AudioClip clip, bool playOneShot)
        {
            if (coroutine_check_unload == null)
                coroutine_check_unload = CoroutineStarter.Start(CheckReadyUnloadClip());

            if (clip == null) return;
            AudioController controller = GetAuidoController(controllerName);
            if (controller == null)
                throw new Exception(string.Format("查询AudioController:{0}失败!", controllerName)); 
            
            controller.Play(clip, playOneShot, OnAudioClipPlayEnd);
            AddRefCount(clip);
        }

        /// <summary>
        /// 在某一个位置播放音频
        /// </summary>
        /// <param name="controllerName"></param>
        /// <param name="clip"></param>
        /// <param name="position"></param>
        /// <exception cref="Exception"></exception>
        public static void PlayAtPoint(string controllerName, AudioClip clip, Vector3 position) {

            if (coroutine_check_unload == null)
                coroutine_check_unload = CoroutineStarter.Start(CheckReadyUnloadClip());

            if (clip == null) return;
            AudioController controller = GetAuidoController(controllerName);
            if (controller == null)
                throw new Exception(string.Format("查询AudioController:{0}失败!", controllerName));
            
            controller.PlayAtPoint(clip, position, OnAudioClipPlayEnd);
            AddRefCount(clip);
        }

        /// <summary>
        /// 暂停播放
        /// </summary>
        /// <param name="controllerName">AudioController名称</param>
        /// <param name="pause">true:暂停 false:取消暂停</param>
        /// <exception cref="Exception"></exception>
        public static void Pause(string controllerName,bool pause) {
            AudioController controller = GetAuidoController(controllerName);
            if (controller == null)
                throw new Exception(string.Format("查询AudioConfig:{0}失败!", controllerName));
            
            controller.Pause(pause);
        }

        /// <summary>
        /// 停止播放
        /// </summary>
        /// <param name="controllerName">AudioController名称</param>
        public static void Stop(string controllerName) {
            AudioController controller = GetAuidoController(controllerName);
            if (controller == null) return; 
            controller.Stop();
        }

        /// <summary>
        /// 添加引用数量
        /// </summary>
        /// <param name="clip"></param>
        private static void AddRefCount(AudioClip clip)
        {
            if (clip == null) return;

            if (AuidoClipRefCount.ContainsKey(clip))
                AuidoClipRefCount[clip]++;
            else
                AuidoClipRefCount.Add(clip, 1);

            // 取消卸载
            if (ReadyUnloadClip.ContainsKey(clip))
                ReadyUnloadClip.Remove(clip);


        }

        private static void RemoveRefCount(AudioClip clip)
        {
            if (clip == null) return;

            if (!AuidoClipRefCount.ContainsKey(clip)) return;
            AuidoClipRefCount[clip]--;

            if (AuidoClipRefCount[clip] <= 0)
            {
                // 准备卸载
                if (!ReadyUnloadClip.ContainsKey(clip))
                    ReadyUnloadClip.Add(clip, Time.time);
            }

        }

        private static void OnAudioClipPlayEnd(AudioClip clip)
        {
            //Debug.LogFormat("音频播放结束!{0}",Time.time);
            RemoveRefCount(clip);
        }

        private static IEnumerator CheckReadyUnloadClip() 
        {
            while (true)
            {
                TempAudioClipList.Clear();
                foreach (var item in ReadyUnloadClip.Keys)
                {
                    TempAudioClipList.Add(item);
                }

                foreach (var item in TempAudioClipList)
                {
                    yield return null;
                    if (!ReadyUnloadClip.ContainsKey(item)) continue;
                    if (Time.time - ReadyUnloadClip[item] >= UNLOAD_TIME_OUT)
                    {
                        // 卸载资源
                        AssetBundleManager.UnloadAsset(item);
                        ReadyUnloadClip.Remove(item);
                    }
                }

                yield return new WaitForSeconds(10); // 10秒钟检测一次
            } 
        }

#endregion

        #region 字段

        [SerializeField]
        [Tooltip("使用当前的AudioSource来播放音频")]
        private AudioSource audioSource;

        [SerializeField]
        [Tooltip("使用哪一个Controller来播放音频")]
        private string controllerName;

        [SerializeField]
        [Tooltip("音频资源类型")]
        private AudioAssetType audioAssetType;

        [SerializeField]
        [Tooltip("音频所在的资源模块名称")]
        private string projectName;
        [SerializeField]
        [Tooltip("音频资源名称")]
        private string assetName;

        [SerializeField]
        [Tooltip("需要播放的音频文件")]
        private AudioClip clip;

        [SerializeField]
        [Tooltip("音频播放类型")]
        private AudioPlayType audioPlayType;

        [SerializeField]
        [Tooltip("音频播放位置")]
        private AudioPlayPosition audioPlayPosition;

        [SerializeField]
        [Tooltip("音频播放位置")]
        private Vector3 playPosition;

        [SerializeField]
        [Tooltip("在Awake中自动调用播放")]
        private bool playOnAwake = false;

        private string generateAudioControllerName;

        #endregion

        #region 属性

        private AudioController AudioController => AudioPlayer.GetAuidoController(controllerName);

        private AudioClip CurrentUseClip
        {
            get 
            {
                if (audioAssetType == AudioAssetType.AudioClip)
                    return clip;
                else { 
                    AudioClip clip = AssetBundleManager.LoadAsset<AudioClip>(projectName, assetName);
                    return clip;
                }
            }
        }
         
        /// <summary>
        /// 当前AudioPlayer使用哪个AudioController来播放音频
        /// </summary>
        public string ControllerName
        {
            get { 
                return controllerName;
            }
            set {
                if (controllerName.Equals(value)) return;
                if (AudioController != null) 
                {
                    AudioController.onVolumeValueChange -= OnControllerVolumeChange;
                    AudioController.onMuteValueChange -= OnControllerMuteChange;
                }
                controllerName = value;
                if (AudioController != null)
                {
                    AudioController.onVolumeValueChange += OnControllerVolumeChange;
                    AudioController.onMuteValueChange += OnControllerMuteChange;
                }
            }
        }

        /// <summary>
        /// 音频资源类型,(AssetBundle Or AudioClip)
        /// </summary>
        public AudioAssetType AudioAssetType
        {
            get 
            {
                return audioAssetType;
            }
            set {
                audioAssetType = value;
            }
        }

        /// <summary>
        /// 音频所在的资源模块名称(AudioAssetType为AssetBundle时有效)
        /// </summary>
        public string ProjectName
        {
            get {
                return projectName;
            }
            set {
                projectName = value;
            }
        }

        /// <summary>
        /// 音频资源名(AudioAssetType为AssetBundle时有效)
        /// </summary>
        public string AssetName
        {
            get {
                return assetName;
            }
            set {
                assetName = value;
            }
        }

        /// <summary>
        /// 音频(AudioAssetType为AudioClip时有效)
        /// </summary>
        public AudioClip Clip
        {
            get {
                return clip;
            }
            set {
                clip = value;
            }
        }
         
        /// <summary>
        /// 音频播放类型(Play Or PlayOneShot)
        /// </summary>
        public AudioPlayType AudioPlayType
        {
            get {
                return audioPlayType;
            }
            set { 
                audioPlayType = value;
            }
        }

        /// <summary>
        /// 音频播放位置类型(IgnorePosition:忽略位置,CurrentTransfomrPosition:当前游戏物体的世界坐标,CustomPosition:自定义位置)
        /// </summary>
        public AudioPlayPosition AudioPlayPosition
        {
            get {
                return audioPlayPosition;
            }
            set {
                audioPlayPosition = value;
            }

        }
        
        /// <summary>
        /// 音频播放的自定义位置
        /// </summary>
        public Vector3 PlayPosition
        {
            get {
                return playPosition;
            }
            set { 
                playPosition = value;
            }
        } 

#endregion

        #region 生命周期

        private void Awake()
        {
            audioSource = transform.GetComponent<AudioSource>();
            if (audioSource != null)
            { 
                audioSource.clip = null;
                generateAudioControllerName = string.Format("{0}:{1}",transform.name,audioSource.GetHashCode());
                RegisterAudioController(generateAudioControllerName, audioSource);

                if (AudioController != null)
                {
                    AudioController.onVolumeValueChange += OnControllerVolumeChange;
                    AudioController.onMuteValueChange += OnControllerMuteChange;
                }
            }

            if (playOnAwake)
                Play();

        }
         
        private void OnDestroy() 
        {
            Stop();
            if (audioSource != null)
                RemoveAudioController(generateAudioControllerName);
        }

        #endregion

        #region 方法

        /// <summary>
        /// 播放
        /// </summary>
        /// <exception cref="Exception"></exception>
        public void Play() {

            if (AudioController == null)
                throw new Exception(string.Format("未查询到AudioController:{0}", controllerName)); 
 
            if (audioSource != null)
            {
                audioSource.volume = AudioController.Volume;
                audioSource.mute = AudioController.Mute;
            }

            switch (audioPlayPosition)
            {
                case AudioPlayPosition.IgnorePosition:
                    Play(audioSource != null ? generateAudioControllerName : controllerName, CurrentUseClip, audioPlayType == AudioPlayType.PlayOneShot);
                    break;
                case AudioPlayPosition.CurrentTransfomrPosition:
                    PlayAtPoint(audioSource != null ? generateAudioControllerName : controllerName, CurrentUseClip, transform.position);
                    break;
                case AudioPlayPosition.CustomPosition:
                    PlayAtPoint(audioSource != null ? generateAudioControllerName : controllerName, CurrentUseClip, playPosition);
                    break;
            }
        }

        /// <summary>
        /// 暂停
        /// </summary>
        /// <param name="pause"></param>
        public void Pause(bool pause)
        {
            Pause(audioSource != null ? generateAudioControllerName : controllerName, pause);
        }

        /// <summary>
        /// 停止播放
        /// </summary>
        public void Stop()
        {
            Stop(audioSource != null ? generateAudioControllerName : controllerName);
        }

        private void OnControllerVolumeChange(float value)
        {
            if (audioSource == null) return;
            audioSource.volume = value;
        }

        private void OnControllerMuteChange(bool mute)
        {
            if (audioSource == null) return;
            audioSource.mute = mute;
        }

#endregion
    }
}


